#include "Servidor.h"

int FRAMES_POR_SEG = 25;
Uint32 MILSOBREFPS    = 1000 / FRAMES_POR_SEG;

Servidor::Servidor(int puerto, Juego juego, string nivel, int cantidadJugadores) {
	
	log_servidor.open(LOG_SERVIDOR);
	log_servidor << "Abierto log servidor\n" << flush;
	
	this->buscador = new BuscadorCliente(puerto);
	if (buscador->getEstado() == NoAceptaConexiones) {
		log_servidor << "Error en conexion\n" << flush;
		delete buscador;
		return;
	}
	
	log_servidor << "Creado BuscadorCliente\n" << flush;
	this->modelo = new Modelo(juego, nivel);
	log_servidor << "Creado modelo\n" << flush;
	this->controlador = new ControladorConexiones(cantidadJugadores);
	log_servidor << "Creado ControladorConexiones\n" << flush;
    
    log_servidor << "Servidor inicializado correctamente\n" << flush;
	
}

Servidor::~Servidor() {
	
	log_servidor << "Eliminando servidor\n" << flush;
	log_servidor.close();
	delete modelo;	
}

void Servidor::Sincronizar(int cliente) {
	
	Sincronizador* sincronizador = new Sincronizador();
	sincronizador->ServidorExponerCarpeta(cliente, PATH_SINCRONIZACION_SERVIDOR);
	delete sincronizador;
}

void Servidor::Correr(unsigned int modo) {


	log_servidor << "Iniciando búsqueda de clientes\n" << flush;
	buscador->ComenzarBusqueda(this, BuscarClientes);
	log_servidor << "Iniciando controlador de clientes\n" << flush;
	controlador->ComenzarControl(this, ManejoConexiones);

	log_servidor << "El estado del juego es " << controlador->getEstadoJuego() << endl << flush;
	
	SDL_Event event;
	
	while (controlador->getEstadoJuego() == JuegoNoComenzo) {
            SDL_Delay( 20 );
            //Si esta jugando en modo MULTI PLAYER
            if ( modo == 1 ) {
				while (SDL_PollEvent(&event)) {
					SDL_Delay( 20 );
					if( event.type == SDL_QUIT ) {
						log_servidor << "Cierre forzado del servidor. Finalizando Juego.\n" << flush;
						TerminarJuego();
						return;
					}
				}
			}
	}
	
	log_servidor << "El estado del juego es " << controlador->getEstadoJuego() << endl << flush;
	
	timer.start();
	Uint32 inicio;
	
	while (controlador->getEstadoJuego() == Jugando) {
		
		//Si esta jugando en modo MULTI PLAYER
        if ( modo == 1 ) {
			while (SDL_PollEvent(&event)) {
				if( event.type == SDL_QUIT )
				{
					log_servidor << "Cierre forzado del servidor. Finalizando Juego\n" << flush;
					TerminarJuego();
					return;
				}
			}
		}

    	inicio = timer.getTick();

        if ( timer.getTick()-inicio < MILSOBREFPS )
            SDL_Delay( MILSOBREFPS - (timer.getTick()-inicio) );
        
        modelo->ActualizarEstaticos();
        modelo->ActualizarMovibles();
        
        vector< pair < pair<idImagen_t, int> , Punto> > dibujablesEstaticos = modelo->getDibujablesEstaticos(timer.getTick());
        vector< pair < pair<idImagen_t, int> , Punto> > dibujablesMovibles = modelo->getDibujablesMovibles(timer.getTick());
        string mensajeObjetos = modelo->armarPaqueteObjetos(dibujablesEstaticos, dibujablesMovibles);
        
        controlador->LockPersonajes();
        modelo->ActualizarPersonajes();
        
        controlador->LockConexiones();
        
        vector<Personaje*> personajes = controlador->getPersonajes();	
        vector< pair < pair<idImagen_t, int> , Punto> > dibujablesPersonajes = modelo->getDibujablesPersonajes(personajes, timer.getTick());
        string mensajePersonajes = modelo->armarPaquetePersonajes(dibujablesPersonajes);
        string mensajePuntajes = modelo->armarPuntajes(personajes);       
        controlador->setPaquetes(mensajePersonajes, mensajeObjetos, mensajePuntajes);
        
        vector< pair<vector< pair< int, int> >, Punto> > sonidosPersonajes = modelo->getSonidosPersonajes(personajes);
        string mensajeSonidos = modelo->armarSonidos(sonidosPersonajes);
        controlador->setSonidos(mensajeSonidos);
        
        if (modelo->EsFinJuego()) {
			controlador->setNombreGanador(modelo->getNombreGanador());
			controlador->setEstadoJuego(FinJuego);
			controlador->UnlockConexiones();
			controlador->UnlockPersonajes();
			break;
		}
        
        controlador->UnlockConexiones();
        controlador->UnlockPersonajes();
   }
   
    log_servidor << "Fin del bucle principal del juego\n" << flush;
    log_servidor << "El estado del juego es " << controlador->getEstadoJuego() << endl << flush;
	
	TerminarJuego();

}

void Servidor::TerminarJuego() {
	
	log_servidor << "Terminando juego\n" << flush;
	
	buscador->setEstado(NoAceptaConexiones);
	shutdown(getBuscador()->getSocketFd(), 2);
	close(getBuscador()->getSocketFd());
	getBuscador()->log_buscador << "Socket cerrado\n" << flush;
	
	if (controlador->getEstadoJuego() != FinJuego &&
		controlador->getEstadoJuego() != ProximoJuego)
			controlador->setEstadoJuego(FinForzado);
	
	void* statusBuscador;
	void* statusControlador;
	log_servidor << "Esperando cierre de buscador de clientes\n" << flush;
	pthread_join(buscador->getThread(), &statusBuscador);
	log_servidor << "Esperando cierre de controlador de clientes\n" << flush;
	pthread_join(controlador->getThread(), &statusControlador);
	
	log_servidor << "Listo para terminar\n" << flush;
}

BuscadorCliente* Servidor::getBuscador() {
	
	return buscador;
}

Modelo* Servidor::getModelo() {
	
	return modelo;
}

Timer Servidor::getTimer() {
	
	return timer;
}

ControladorConexiones* Servidor::getControladorConexiones() {
	
	return controlador;
}


// ********************************************************************
// Funciones de threads
// ********************************************************************
	
void* BuscarClientes (void* data) {

	Servidor* servidor = (Servidor*) data;
	vector<pthread_t> clientesPotenciales;
	listen(servidor->getBuscador()->getSocketFd(), 5);
	
	while (true) {
		
        usleep(30000);
		servidor->getBuscador()->log_buscador << "El estado del buscador es " << servidor->getBuscador()->getEstado() << endl << flush;
		
		int socket;
		while ( (socket = servidor->getBuscador()->BuscarCliente()) < 0 ) {
            usleep(30000);
			if (servidor->getBuscador()->getEstado() == AceptaConexiones) {
				servidor->getBuscador()->log_buscador << "Error al aceptar cliente\n" << flush;
			}
			else {
				servidor->getBuscador()->log_buscador << "Esperando fin de clientes potenciales\n" << flush;
				while (servidor->getBuscador()->getClientesPotenciales() != 0) {
					usleep(30000);
					continue;
				}
				servidor->getBuscador()->log_buscador << "Finalizada busqueda de clientes\n" << flush;
				return NULL;	
			}
		}
		
		servidor->getBuscador()->log_buscador << "Cliente aceptado\n" << flush;
		ClientePotencial_t cliente = { socket, servidor };
		servidor->getBuscador()->CrearClientePotencial(&cliente, IngresoCliente);
		
	}
	
	return NULL;
	
}

void* IngresoCliente(void* data) {
	
	Servidor* servidor = ((ClientePotencial_t*) data)->servidor;
	int cliente = ((ClientePotencial_t*) data)->socket;
	
	servidor->Sincronizar(cliente);
	
	if (servidor->getBuscador()->getEstado() == RechazaConexiones) {
		writeSocket(cliente, "<No>");
		shutdown(cliente, 2);
		close(cliente);
		servidor->getBuscador()->EliminarClientePotencial();
		return NULL;
	}
	else {
		int res = writeSocket(cliente, "<Si>");
		if (res == SIN_CONEXION) {
			shutdown(cliente, 2);
			close(cliente);
			servidor->getBuscador()->EliminarClientePotencial();
			return NULL;
		}
	}
	
	string mensaje = servidor->getBuscador()->CodificarInfoImagenes(GestionId::getInfoImagenes());
	int res = writeSocket(cliente, mensaje);
	if (res == SIN_CONEXION) {
		shutdown(cliente, 2);
		close(cliente);
		servidor->getBuscador()->EliminarClientePotencial();
		return NULL;
	}
	
	mensaje = servidor->getBuscador()->CodificarInfoSonidos(GestionSonidos::getInfoSonidos());
	res = writeSocket(cliente, mensaje);
	if (res == SIN_CONEXION) {
		shutdown(cliente, 2);
		close(cliente);
		servidor->getBuscador()->EliminarClientePotencial();
		return NULL;
	}
	
	mensaje = "<" + Logger::intToString(servidor->getModelo()->getNivel()->getAncho()) + ";";
	mensaje += Logger::intToString(servidor->getModelo()->getNivel()->getAlto()) + "#" + ">";
	res = writeSocket(cliente, mensaje);
	
	if (res == SIN_CONEXION) {
		shutdown(cliente, 2);
		close(cliente);
		servidor->getBuscador()->EliminarClientePotencial();
		return NULL;
	}
		
	vector< pair<int, TipoPersonaje*> > personajesHabilitados = servidor->getControladorConexiones()->InfoPersonajesHabilitados();
	mensaje = servidor->getBuscador()->CodificarPersonajesHabilitados(personajesHabilitados);
	res = writeSocket(cliente, mensaje);
	
	if (res == SIN_CONEXION) {
		shutdown(cliente, 2);
		close(cliente);
		servidor->getBuscador()->EliminarClientePotencial();
		return NULL;
	}
		
	int tipoPersonaje;
	while (true) {
		
		SDL_Delay(DELAY_CABEZA_SERVER);

		if (servidor->getBuscador()->getEstado() == NoAceptaConexiones) {
			shutdown(cliente, 2);
			close(cliente);
			break;
		}
		
		int largo;
		ioctl(cliente, FIONREAD, &largo);
		if (largo == 0) continue;
		
		string respuesta;
		int res = readSocket(cliente, &respuesta, largo);
		
		if (res == SIN_CONEXION) {
			shutdown(cliente, 2);
			close(cliente);
			servidor->getBuscador()->EliminarClientePotencial();
			break;
		}
			
		tipoPersonaje = atoi(respuesta.substr(0, respuesta.find_first_of("#")).c_str());
		if (servidor->getControladorConexiones()->OcuparPersonaje(tipoPersonaje)) {
			int res = writeSocket(cliente, "<Si>");
			
			if (res == SIN_CONEXION) {
				shutdown(cliente, 2);
				close(cliente);
				break;
			}
			
			servidor->getBuscador()->log_buscador << "Creando personaje con tipo " << GestionTiposPersonaje::getListaTipos().at(tipoPersonaje) << endl << flush;
	
			ControladorCliente* controladorCliente = new ControladorCliente(cliente, tipoPersonaje, servidor->getModelo()->getNivel()->getPiso(), servidor->getModelo()->getNivel()->getAncho(), servidor->getModelo()->getNivel()->getAlto(), servidor->getModelo()->getNivel()->getGravedad() ,servidor->getControladorConexiones()->getEstadoJuego());
			//ControladorCliente* controladorCliente = new ControladorCliente(cliente, tipoPersonaje, 0, servidor->getModelo()->getNivel()->getAncho(), servidor->getModelo()->getNivel()->getAlto(), servidor->getModelo()->getNivel()->getGravedad() ,servidor->getControladorConexiones()->getEstadoJuego());
			if (!servidor->getControladorConexiones()->AgregarConexion(controladorCliente)) {
				servidor->getBuscador()->setEstado(RechazaConexiones);
				writeSocket(cliente, "<No>");
				shutdown(cliente, 2);
				close(cliente);
				break;
			}
			else {
				int res = writeSocket(cliente, "<Si>");
					
				if (res == SIN_CONEXION) {
					shutdown(cliente, 2);
					close(cliente);
					break;
				}
			}
				
			controladorCliente->ManejarCliente();
			string respuesta; // "OK#" cuando termina de cargar las imagenes
			
			int largoMensaje = 0;
			Uint32 timeout = 60000*5; // maximo 5 min para cargar las imagenes
			Timer timer;
			timer.start();
			ioctl(controladorCliente->getSocket(), FIONREAD, &largoMensaje);		
			while (largoMensaje == 0) {
				ioctl(controladorCliente->getSocket(), FIONREAD, &largoMensaje);
				if (servidor->getControladorConexiones()->getEstadoJuego() == FinForzado) break;
				if (controladorCliente->getEstadoConexion() == Inactiva) break;
				if (timer.getTick() > timeout) break;
			}

			res = readSocket(cliente, &respuesta, 3);
			if (res == SIN_CONEXION) {
				controladorCliente->setEstadoConexion(Inactiva);
			}
			
			controladorCliente->EnviarMensaje(servidor->getModelo()->armarPaqueteObstaculos(), "O");
			
			servidor->getModelo()->addPersonaje(controladorCliente->getControladorLectura()->getControlador()->getJugador());
			controladorCliente->getControladorLectura()->setEstadoLectura(LecturaCorriendo);
			break;
		}
		else {
			int res = writeSocket(cliente, "<No>");
			
			if (res == SIN_CONEXION) {
				shutdown(cliente, 2);
				close(cliente);
				break;
			}
			
			personajesHabilitados = servidor->getControladorConexiones()->InfoPersonajesHabilitados();
			mensaje = servidor->getBuscador()->CodificarPersonajesHabilitados(personajesHabilitados);
			res = writeSocket(cliente, mensaje);
			
			if (res == SIN_CONEXION) {
				shutdown(cliente, 2);
				close(cliente);
				break;
			}
		}
	}
	
	servidor->getBuscador()->EliminarClientePotencial();
	
	return NULL;
	
}

void* ManejoConexiones(void* data) {

	ControladorConexiones* controlador = ((Servidor*) data)->getControladorConexiones();
	
	controlador->log_controlador << "Iniciando Manejo de Conexiones\n" << flush;
	controlador->log_controlador << "El estado del juego es " << controlador->getEstadoJuego() << endl << flush;
	controlador->log_controlador << "Cantidad de conexiones es " << controlador->CantidadConexiones() << endl << flush;
	
	while (controlador->getEstadoJuego() == JuegoNoComenzo) {
		SDL_Delay(DELAY_CABEZA_SERVER);
		if (controlador->getEstadoJuego() == FinForzado) return NULL;
		controlador->ManejarEstados();
		if (controlador->ListoParaEmpezar()) {
			controlador->ComenzarJuego();
			break;
		}
	}
	
	controlador->log_controlador << "El estado del juego es " << controlador->getEstadoJuego() << endl << flush;
	
	while (true) {
		SDL_Delay(DELAY_CABEZA_SERVER);
		controlador->ManejarEstados();	
		if (controlador->getEstadoJuego() == ProximoJuego) break;
	}
	
	controlador->log_controlador << "Finalizado control de conexiones\n" << flush;
	
	return NULL;
}

